Skip to content

feat(plans): strict-≥80%-margin tier redesign — retire every unlimited (-1)#262

Merged
mastermanas805 merged 3 commits into
masterfrom
feat/strict-80-margin-retire-unlimited
Jun 5, 2026
Merged

feat(plans): strict-≥80%-margin tier redesign — retire every unlimited (-1)#262
mastermanas805 merged 3 commits into
masterfrom
feat/strict-80-margin-retire-unlimited

Conversation

@mastermanas805
Copy link
Copy Markdown
Member

Rule-22 synchronized pricing redesign (strict-80%, retire unlimited) — see docs/sessions/2026-06-04/PRICING-MARGIN-MODEL-AND-TEAM-REDESIGN.md.

CEO directive (2026-06-05, LOCKED): retire every -1 ("unlimited") limit in api/plans.yaml (source of truth) except provisions_per_day (per-fingerprint rate limit, no per-GB COGS) and apply the locked table. Hard-caps only. Team stays GATED — eventual contract numbers, NOT an un-gate. Above Team = Enterprise (contact sales).

plans.yaml (monthly + *_yearly mirror)

  • anon/free: queue 1024→64 MB; queue_count -1→1 (0 means unlimited via QueueCountLimit, so 1).
  • hobby: queue 5120→2048 MB. pro: queue 10240→5120 MB.
  • growth: vector 20480→10240; redis_commands -1→5M; mongo 20480/50/50k; queue 20480/50; storage 153600; webhooks 100000.
  • team: every -1 → finite (pg 51200/100, vector 30720/100, redis 1536/10M, mongo 40960/50/50k, queue 40960/100, storage 307200, webhooks 100000, members 25, vault 1000, deploys 100).

Other surfaces

  • openapi.go: drop "$199 unlimited" + "team is unlimited" wording.
  • usage_wall.go: remove the team-tier short-circuit — Team is finite now and has quota walls like every other tier, so it falls through to the audit query.
  • Regression test TestPlansYAML_NoUnlimitedExceptProvisionsPerDay: reflects over every Limits int field on every tier; fails the build if any -1 reappears (or a new int field is added with -1) except provisions_per_day.
  • Synced pinning tests (QueueCountLimit, vector, deploy/queue caps, webhook; -1→10000 clamp arm now covered via a synthetic registry).

Verify

go build/vet ./... clean; go test ./internal/plans/... green; targeted handler tests green. Full go test ./... -short passes except pre-existing NATS/customer-DB/sqlmock env flakes (identical on origin/master baseline — CI provides those services).

Known follow-up (NOT in this PR)

  • provisions_per_day: -1 intentionally retained (rate limit). Separate hardening: per-service resource-COUNT caps don't exist as fields yet → a tenant could still create many resources.
  • Worker quota_wall_nudge.go still skips team-tier from wall evaluation — pairs with this PR's usage_wall.go change; a follow-up should let it write team wall rows.

Companion PRs: common #46, instanode-web, content.

…ed" (-1)

CEO directive (2026-06-05, LOCKED): retire every `-1` ("unlimited") limit in
api/plans.yaml (the source of truth) except provisions_per_day (a
per-fingerprint rate limit, no per-GB COGS) and apply the locked number table.
Hard-caps only (no metered overage). Team stays GATED — these are the eventual
contract numbers, NOT an un-gate. Above Team = Enterprise (contact sales).

plans.yaml changes (monthly + *_yearly mirror):
- anonymous/free: queue 1024→64 MB; queue_count -1→1 (0=unlimited via
  QueueCountLimit, so 1).
- hobby: queue 5120→2048 MB. pro: queue 10240→5120 MB.
- growth: vector 20480→10240; redis_commands -1→5M; mongo 20480/50/50k;
  queue 20480/50; storage 153600; webhooks 100000 (all -1 retired).
- team: every -1 → finite (pg 51200/100, vector 30720/100, redis 1536/10M,
  mongo 40960/50/50k, queue 40960/100, storage 307200, webhooks 100000,
  members 25, vault 1000, deploys 100).

Other surfaces (rule 22):
- openapi.go: drop "$199 unlimited" + "team is unlimited" wording.
- usage_wall.go: remove the team-tier short-circuit (Team is finite now, so it
  has quota walls like every other tier) — falls through to the audit query.
- Registry-iterating regression: TestPlansYAML_NoUnlimitedExceptProvisionsPerDay
  reflects over every Limits int field on every tier and fails the build if any
  -1 reappears (or a new int field is added with -1) except provisions_per_day.
- Synced pinning tests: QueueCountLimit, vector limits, deploys/queue caps,
  webhook (team 100k; -1→10000 clamp arm now covered via a synthetic registry).

Rule-22 synchronized pricing redesign (strict-80%, retire unlimited) — see
docs/sessions/2026-06-04/PRICING-MARGIN-MODEL-AND-TEAM-REDESIGN.md.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@mastermanas805 mastermanas805 enabled auto-merge (squash) June 5, 2026 14:24
mastermanas805 and others added 2 commits June 5, 2026 19:56
snapshot-drift CI gate caught the openapi.go changes (Team "$199 unlimited" →
"$199 finite, not unlimited" + usage-wall "team is unlimited" → "Team has
finite limits"). Regenerated via `make openapi-snapshot` (rule 22 — the
dashboard + instanode-web typed clients depend on this file).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
common #46 retired every unlimited (-1) resource limit to finite caps
(Team/Growth now finite). Four api handler tests still assumed the old
"team/growth = unlimited" reality and reded build-and-test, which broke
api master (every PR checks out common@master with the new numbers).

Fixes (TESTS only — the -1-handling production code is preserved as
correct defensive utilities, still reachable via provisions_per_day: -1
and future tiers):

- billing_usage_coverage: TestBillingUsage_UnlimitedTier_MbToBytesNegative
  routed the team tier (was -1) through the HTTP usage handler to hit
  mbToBytes(-1). No real tier carries -1 now, so the -1 path can't be
  reached via a tier. Moved to a direct internal test
  (mb_to_bytes_internal_test.go) that feeds synthetic -1/finite inputs —
  keeps the defensive "-1 -> ∞" branch covered.
- misc_routes_block_integration: the "team tier short-circuits to
  near_wall=false" subtest assumed the unlimited early-return. That
  early-return was removed (Team is finite -> walls apply); subtest now
  asserts near_wall=true with a seeded wall row.
- small_handlers_final: TestUsageWallFinal_DBError_503 used failAfter=1
  (team-tier pre-query #1 then audit query #2). With the early-return
  gone the audit query is the FIRST DB call -> failAfter=0.
- team_coverage_mock: TestTeamMembers_InviteMember_LegacyMemberSuccess
  assumed team_members=-1 so withinMemberLimit skipped the count query.
  Team is now 25 -> added the teamSeatTotal (members + pending) mock
  expectations; 1 seat < 25 -> within limit -> 201.

New rule-18 guard (strict_margin_finite_limits_test.go): iterates the
LIVE plans.yaml registry and fails if any tier ships a -1 on any costed
resource limit (only provisions_per_day may be -1). Re-introducing an
unlimited resource cap — or a new tier with one — now reds here instead
of silently shipping unbounded COGS.

Team stays GATED (no checkout change). Unblocks api master.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@mastermanas805 mastermanas805 force-pushed the feat/strict-80-margin-retire-unlimited branch from aeea24b to f358aaf Compare June 5, 2026 17:09
@mastermanas805 mastermanas805 merged commit 6aefa52 into master Jun 5, 2026
19 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant